package com.lwq.code;

import com.lwq.constants.ChannelAttr;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshaker;
import io.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.CharsetUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.io.IOException;

/**
 * Description:
 * Datetime: 2021/6/15
 *
 * @author LiuWenQing
 */
@Component
public class EventChannelHandler extends SimpleChannelInboundHandler<Object> {

    @Value("${netty.server.host}")
    private String HOST;

    private String[] accounts = new String[]{"liuwenqing", "zhangsan", "lisi", "wangwu", "zhaoliu", "jia", "yi", "bing", "ding", "abc"};

    private Logger logger = LoggerFactory.getLogger(EventChannelHandler.class);

    private static EventChannelHandler eventChannelHandler;

    @PostConstruct
    public void init() {
        eventChannelHandler = this;
    }


    /**
     * 连接上服务器
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
        System.out.println(WebSocketUserUtil.getUserName(ctx.channel()) + " ======= >" + ctx.channel().id() + "已连接上服务器");
    }

    /**
     * 活跃的通道  也可以当作用户连接上客户端进行使用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        ctx.channel().attr(ChannelAttr.UID).set(ctx.channel().id().asShortText());
    }

    /**
     * 这里只要完成 flush
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        ctx.flush();
    }

    /**
     * 不活跃的通道  就说明用户失去连接
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        System.out.println(ctx.channel().id() + "已失去链接");
    }

    /**
     * 断开连接
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        WebSocketUserUtil.removeChannelByChannel(ctx.channel());
        release(ctx);
    }

    /**
     * 连接异常需要关闭相关资源
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        this.logger.error(cause.getMessage(), cause);
        WebSocketUserUtil.removeChannelByChannel(ctx.channel());
        release(ctx);
    }

    /**
     * 收发消息处理
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws IOException {
        if (msg instanceof HttpRequest) {
            doHandlerHttpRequest(ctx, (HttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            doHandlerWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    /**
     * websocket消息处理
     */
    private void doHandlerWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame msg) throws IOException {
        if (msg instanceof TextWebSocketFrame) {
            TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) msg;
            if (ctx.channel().isWritable()) {
                String text = textWebSocketFrame.text();
                if (Heartbeats.isPing(text)) {
                    // 如果是心跳则返回心跳
                    ctx.channel().writeAndFlush(Heartbeats.heartbeatContent());
                } else {
                }
            } else {
                try {
                    ctx.channel().writeAndFlush(Heartbeats.heartbeatContent()).sync();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * wetsocket第一次连接握手
     */
    private void doHandlerHttpRequest(ChannelHandlerContext ctx, HttpRequest msg) {
        // http 解码失败
        if (!msg.getDecoderResult().isSuccess()) {
            if (WebSocketUserUtil.isShowLog) {
                logger.info("Socket http 解码失败");
            }
            sendHttpResponse(ctx, (FullHttpRequest) msg, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
            return;
        }
        String userId = WebSocketUserUtil.getUserId(msg);
        if (StringUtils.isEmpty(userId)) {
            ctx.writeAndFlush(new TextWebSocketFrame("缺少用户id,请登录"));
            release(ctx);
            return;
        }
        Channel channel = ctx.channel();
        if (WebSocketUserUtil.isShowLog) {
            logger.info("创建连接：id = " + userId + "   channel id = " + channel.id());
        }
        WebSocketUserUtil.getChannelMap().put(userId + "," + channel.id(), channel);
        WebSocketServerHandshakerFactory factory = new WebSocketServerHandshakerFactory("ws://" + msg.headers().get("Host") + HOST, null, false,
                65536 * 10);
        WebSocketServerHandshaker handshaker = factory.newHandshaker(msg);
        if (handshaker == null) {
            WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
            return;
        }
        //进行连接
        handshaker.handshake(channel, (FullHttpRequest) msg);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
        if (!(evt instanceof IdleStateEvent)) {
            return;
        }
        IdleStateEvent idleEvent = (IdleStateEvent) evt;
        String uid = ctx.channel().attr(ChannelAttr.UID).get();
        /*
         * 关闭未认证的连接
         */
        if (idleEvent.state() == IdleState.WRITER_IDLE && uid == null) {
            ctx.close();
            return;
        }

        /*
         * 已经认证的连接发送心跳请求
         */
        if (idleEvent.state() == IdleState.WRITER_IDLE && uid != null) {

            Integer pingCount = ctx.channel().attr(ChannelAttr.PING_COUNT).get();
            ctx.channel().attr(ChannelAttr.PING_COUNT).set(pingCount == null ? 1 : pingCount + 1);
            ctx.channel().writeAndFlush(Heartbeats.heartbeatContent());
            return;
        }

        /*
         * 如果心跳请求发出30秒内没收到响应，则关闭连接
         */
        Integer pingCount = ctx.channel().attr(ChannelAttr.PING_COUNT).get();
        if (idleEvent.state() == IdleState.READER_IDLE && pingCount != null && pingCount >= 3) {
            ctx.close();
            logger.info("{} pong timeout.", ctx.channel());
        }
    }

    private static void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res) {
        // 返回应答给客户端
        if (res.getStatus().code() != 200) {
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        // 如果是非Keep-Alive，关闭连接
        ChannelFuture f = ctx.channel().writeAndFlush(res);
        if (!HttpHeaders.isKeepAlive(req) || res.getStatus().code() != 200) {
            f.addListener(ChannelFutureListener.CLOSE);
        }
    }

    private void release(ChannelHandlerContext ctx) {
        if (ctx != null) {
            ctx.close();
            ctx.channel().close();
        }
    }

}
